<?php

namespace app\common\command;

use app\common\model\Order as OrderModel;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Cache;
use think\facade\Log;

class AutoUpdateOrder extends Command
{
    //交易地址
    static protected $apiUrl = 'https://apilist.tronscanapi.com/api/token_trc20/transfers';
    static protected $localAddress;
    public $startTime;
    public $endTime;
    protected $redis;

    protected function configure()
    {
        // 指令配置
        $this->setName('auto-update-order')
            ->setDescription('更新订单状态');
    }

    protected function execute(Input $input, Output $output)
    {
        $validSecond = env('TRONSCAN.validateDataSecond');
        self::$localAddress = env('TRONSCAN.localAddress');
        $this->endTime = round(microtime(true) * 1000);
        $this->startTime = $this->endTime - $validSecond * 1000;
        $this->redis = Cache::store('redis')->handler();

        $startTime = date('Y-m-d H:i:s', round($this->startTime / 1000, 0));
        $endTime = date('Y-m-d H:i:s', round($this->endTime / 1000, 0));

        //从数据库中读取数据
        $dbData = OrderModel::where(['status' => OrderModel::CREATE_ORDER])
            ->whereBetween('created_at', [$startTime, $endTime])
            ->column(['id', 'order_num', 'payment_price']);

        if(empty($dbData)) {
            $output->writeln('数据库暂无订单');
            return false;
        }

        foreach ($dbData as &$datum) {
            if(!empty($datum['payment_price'])) {
                $datum['payment_price'] = $datum['payment_price'] * 1000000;
            }
        }

        $dbData = array_column($dbData, null, 'payment_price');

        //从接口中获取数据
        $apiData = $this->getDataByTronscanApi();

        $updData = [];
        foreach ($apiData as $items) {
            foreach ($items as $item) {
                if(!empty($item['finalResult']) && !empty($item['quant']) && !empty($item['to_address']) && !empty($item['block_ts']) && !empty($item['transaction_id']) && !empty($item['from_address'])) {
                    if($item['finalResult'] == 'SUCCESS' && $item['to_address'] == self::$localAddress) {
                        $thisOrder = !empty($dbData[$item['quant']]) ? $dbData[$item['quant']] : [];
                        if(!empty($thisOrder)) {
                            $updData[] = [
                                'id' => $thisOrder['id'],
                                'status' => OrderModel::ORDER_SUCCEED,
                                'state' => OrderModel::WAIT_STATE,
                                'transaction_id' => $item['transaction_id'],
                                'owner_address' => $item['from_address'],
                                'payment_time' => date('Y-m-d H:i:s', round($item['block_ts'] / 1000, 0)),
                                'payment_details' => json_encode($item, 256)
                            ];

                            //改变redis中的状态
                            $orderStateKey = implode(':', ['order_state', $thisOrder['order_num']]);
                            if($this->redis->exists($orderStateKey)) {
                                $this->redis->set($orderStateKey, $item['transaction_id']);
                            }
                        }
                    }
                }
            }
        }

        if(!empty($updData)) {
            (new OrderModel())->saveAll($updData);
        }

        $output->writeln('执行完毕');
    }


    /**
     * 从接口中获取数据
     * @return \Generator
     */
    protected function getDataByTronscanApi()
    {
        try{
            $apiKey = env('TRONSCAN.APIKEY');
            $address = self::$localAddress;

            $start = 0;
            $limit = 100;
            $totalAmount = 0;
            $hasMore = true;
            $header = ["TRON-PRO-API-KEY:{$apiKey}"];

            while ($hasMore) {
                //获取请求地址
                $url = $this->buildApiUrl($address, $start, $limit);
                //发送请求
                $result = $this->sendRequest($url, $header);
                $response = json_decode($result, true);

                if(empty($response['token_transfers'])) {
                    $hasMore = false;
                }else{
                    $respData = $response['token_transfers'];
                    $totalAmount += count($respData);

                    if($totalAmount >= $response['total'] || count($respData) < $limit) {
                        $hasMore = false;
                    }else{
                        $start += $limit;
                    }

                    yield $respData;
                }
            }
        }catch (\Exception $e){
            $content = <<<LDO
文件：{$e->getFile()}
行号：{$e->getLine()}
异常信息：{$e->getMessage()}
LDO;

            Log::info($content);
        }

        yield [];
    }

    /**
     * 构建请求地址
     * @param $address
     * @param int $start
     * @param int $limit
     * @return string
     */
    protected function buildApiUrl($address, $start = 0, $limit = 100)
    {
        $urlParams = [
            'relatedAddress' => $address,
            'start' => $start,
            'limit' => $limit,
            'sort' => '-timestamp',
            'count' => true,
            'start_timestamp' => $this->startTime,
            'end_timestamp' => $this->endTime,
        ];

        return implode('?', [self::$apiUrl, http_build_query($urlParams)]);
    }


    /**
     * 发送请求
     * @param $url
     * @param array $header
     * @return bool|string
     * @throws \Exception
     */
    protected function sendRequest($url, $header = [])
    {

        // 初始化cURL会话
        $ch = curl_init();

        // 设置cURL选项
        curl_setopt($ch, CURLOPT_URL, $url); // 目标URL
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回结果而不是直接输出

        if(!empty($header)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }

        // 执行cURL会话
        $response = curl_exec($ch);

        // 检查是否有错误发生
        if (curl_errno($ch)) {
            $message = json_encode(curl_getinfo($ch), 256);
            $content = "请求地址: {$url}；异常信息: {$message}";
            throw new \Exception($content);
        }

        // 关闭cURL会话
        curl_close($ch);

        return $response;
    }
}